package com.sunlands.zlcx.usercenter.service;

import com.sunlands.zlcx.usercenter.constant.YN;
import com.sunlands.zlcx.usercenter.constant.dic.FREE_TIME_ACTIVES_RULE;
import com.sunlands.zlcx.usercenter.domain.DicDO;
import com.sunlands.zlcx.usercenter.domain.InviteCodeDO;
import com.sunlands.zlcx.usercenter.repository.InviteCodeRepository;
import com.sunlands.zlcx.usercenter.util.Code;
import com.sunlands.zlcx.usercenter.vo.InviteCodeVO;
import com.sunlands.zlcx.usercenter.vo.UserInviteRuleVO;
import com.sunlands.zlcx.usercenter.vo.UsersVO;
import com.sunlands.zlcx.usercenter.vo.VipUserVO;
import com.sunlands.zlcx.usercenter.vo.response.BusinessResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static com.sunlands.zlcx.usercenter.config.RedisConfig.CacheKeys.USERS_INVITE_CODE_COUNT;
import static com.sunlands.zlcx.usercenter.config.RedisConfig.CacheKeys.USERS_INVITE_CODE_LIST;
import static com.sunlands.zlcx.usercenter.constant.dic.FREE_TIME_ACTIVES_RULE.*;
import static com.sunlands.zlcx.usercenter.vo.CommonResultMessage.INVITE_CODE_NO_VALID;

/**
 * @author shixiaopeng
 */
@Service
@Slf4j
public class UsersInviteServiceImpl {

    @Autowired
    private InviteCodeRepository inviteCodeRepository;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private DicServiceImpl dicService;
    @Autowired
    private UsersServiceImpl usersService;

    public UserInviteRuleVO getRule() {
        //字典数据
        DicDO rule = dicService.findByCode(FREE_TIME_ACTIVES_RULE.dicName());
        UserInviteRuleVO ruleVO = UserInviteRuleVO.builder()
                .giveMinutes(rule.filter(give_minutes.name()).getCode())
                .vipCodeCount(rule.filter(vip_code_count.name()).getCode())
                .noVipCodeCount(rule.filter(no_vip_code_count.name()).getCode())
                .link(rule.filter(link.name()).getData())
                .build();

        return ruleVO;
    }


    @Cacheable(cacheNames = USERS_INVITE_CODE_LIST, key = "#userId +'_'+ #today", unless = "#result == null")
    public List<InviteCodeDO> getCode(Long userId, String today) {
        List<InviteCodeDO> inviteCodeDOList = getInviteCodeDOS(userId, today);
        UsersVO usersVO = usersService.find(userId);
        UserInviteRuleVO rule = getRule();
        Long userCodeCount = rule.getNoVipCodeCount();
        if (YN.YES.getCode().equals(usersVO.getVipStatus())) {
            userCodeCount = rule.getVipCodeCount();
        }

        if (Objects.isNull(inviteCodeDOList) || inviteCodeDOList.size() < userCodeCount) {
            if (Objects.nonNull(inviteCodeDOList)) {
                userCodeCount -= inviteCodeDOList.size();
            }
            inviteCodeDOList.addAll(createCode(userId, today, userCodeCount));
        }
        return inviteCodeDOList;
    }

    public List<InviteCodeDO> getInviteCodeDOS(Long userId, String today) {
        return inviteCodeRepository.findAllByUserIdAndToday(userId, today);
    }

    public long getCount(Long userId, String today) {
        return inviteCodeRepository.countByUserIdAndCreateTime(userId, today);
    }

    @Caching(evict = {
            @CacheEvict(cacheNames = USERS_INVITE_CODE_LIST, key = "#userId +'_'+ #today")
    })
    public List<InviteCodeDO> createCode(Long userId, String today, Long userCodeCount) {

        List<InviteCodeDO> inviteCodeDOS = new ArrayList<>();
        long count = codeCount();
        int len = 6;
        if (count >= 308915776L) {
            len = 7;
        }
        for (int i = 0; i < userCodeCount; i++) {
            InviteCodeDO inviteCodeDO = new InviteCodeDO();
            inviteCodeDO.setUserId(userId);
            inviteCodeDO = getInviteCodeDO(len, inviteCodeDO);
            inviteCodeDOS.add(inviteCodeDO);
        }
        redisTemplate.opsForValue().increment(USERS_INVITE_CODE_COUNT, userCodeCount);
        return inviteCodeDOS;
    }

    @Caching(evict = {
            @CacheEvict(cacheNames = USERS_INVITE_CODE_LIST, key = "#codeDO.userId +'_'+ #today")
    })
    @Transactional(rollbackFor = Exception.class)
    public BusinessResult<InviteCodeVO> relation(InviteCodeDO codeDO, Long userId, String code, String today) {
        findById(codeDO.getId());
        codeDO = findCode(code, today);
        if (Objects.isNull(codeDO) || Objects.nonNull(codeDO.getInviteUserId())) {
            return BusinessResult.createInstance(INVITE_CODE_NO_VALID);
        }
        codeDO.setInviteUserId(userId);
        UserInviteRuleVO rule = getRule();
        codeDO.setGiveMinutes(rule.getGiveMinutes());
        codeDO = inviteCodeRepository.save(codeDO);
        return BusinessResult.createSuccessInstance(InviteCodeVO.fromDO(codeDO));
    }

    @Transactional(rollbackFor = Exception.class)
    public InviteCodeDO findById(Long id) {
        return inviteCodeRepository.findById(id);
    }


    public InviteCodeDO findCode(String code, String today) {
        return inviteCodeRepository.findByCodeAndToday(code, today);
    }

    public InviteCodeDO findCode(String code) {
        return inviteCodeRepository.findByCode(code);
    }

    public InviteCodeDO findByInviteUserId(Long inviteUserId) {
        return inviteCodeRepository.findByInviteUserId(inviteUserId);
    }

    private InviteCodeDO getInviteCodeDO(int len, InviteCodeDO inviteCodeDO) {
        inviteCodeDO.setCode(Code.createShareCode(len));

        try {
            inviteCodeDO = inviteCodeRepository.save(inviteCodeDO);
        } catch (Exception e) {
            log.error("生成邀请码异常", e);
            getInviteCodeDO(len, inviteCodeDO);
        }
        return inviteCodeDO;
    }

    public Long codeCount() {
        Object o = redisTemplate.opsForValue().get(USERS_INVITE_CODE_COUNT);
        long count = 0;
        if (Objects.isNull(o)) {
            count = inviteCodeRepository.count();
            redisTemplate.opsForValue().set(USERS_INVITE_CODE_COUNT, count);
        } else {
            count = Long.valueOf(String.valueOf(o));
        }
        return count;
    }
}
